import ash from "@ash.ts/ash";
import { KeyCode, Node, Prefab, instantiate, misc, resources } from "cc";
import { Animation } from "../component/Animation";
import { Asteroid } from "../component/Asteroid";
import { Bullet } from "../component/Bullet";
import { Collision } from "../component/Collision";
import { DeathThroes } from "../component/DeathThroes";
import { Display } from "../component/Display";
import { GameState } from "../component/GameState";
import { Gun } from "../component/Gun";
import { GunControls } from "../component/GunControls";
import { Hud } from "../component/Hud";
import { Motion } from "../component/Motion";
import { MotionControls } from "../component/MotionControls";
import { Position } from "../component/Position";
import { Spaceship } from "../component/Spaceship";
import { WaitForStart } from "../component/WaitForStart";
import { AsteroidDeathView } from "../display/AsteroidDeathView";
import { AsteroidView } from "../display/AsteroidView";
import { BulletView } from "../display/BulletView";
import { SpaceshipDeathView } from "../display/SpaceshipDeathView";
import { SpaceshipView } from "../display/SpaceshipView";

export class EntityCreator {
    private _engine: ash.Engine;
    private _waitEntity!: ash.Entity;
    private _spaceship!: ash.Entity;

    public constructor(engine: ash.Engine) {
        this._engine = engine;
    }

    public destroyEntity(entity: ash.Entity): void {
        this._engine.removeEntity(entity);
    }

    public createGame(): ash.Entity {
        const res = resources.get("prefab/Hud", Prefab);
        if (res) {
            const hud = instantiate(res);
            const gameEntity: ash.Entity = new ash.Entity('game')
                .add(new GameState())
                .add(new Hud(hud))
                .add(new Display(hud))
                .add(new Position(0, 0, 0));
            this._engine.addEntity(gameEntity);
            return gameEntity;
        }
        return null;
    }

    public createWaitForClick(): ash.Entity {
        if (!this._waitEntity) {
            const res = resources.get("prefab/WaitForStart", Prefab);
            if (res) {
                const waitView = instantiate(res);
                this._waitEntity = new ash.Entity('wait')
                    .add(new WaitForStart(waitView))
                    .add(new Display(waitView))
                    .add(new Position(0, 0, 0));
            }
        }
        if (this._waitEntity) {
            this._waitEntity.get(WaitForStart)!.startGame = false;
            this._engine.addEntity(this._waitEntity);
        }
        return this._waitEntity;
    }

    public createAsteroid(radius: number, x: number, y: number): ash.Entity {
        const asteroid: ash.Entity = new ash.Entity();

        const fsm: ash.EntityStateMachine = new ash.EntityStateMachine(asteroid);

        const velocityX = (Math.random() - 0.5) * 4 * (50 - radius);
        const velocityY = (Math.random() - 0.5) * 4 * (50 - radius);
        const angularVelocity: number = Math.random() * 2 - 1;

        let node = new Node();
        node.addComponent(AsteroidView).radius = radius;
        fsm.createState('alive')
            .add(Motion)
            .withInstance(new Motion(velocityX, velocityY, angularVelocity))
            .add(Collision)
            .withInstance(new Collision(radius))
            .add(Display)
            .withInstance(new Display(node));

        node = new Node();
        const deathView = node.addComponent(AsteroidDeathView);
        deathView.radius = radius;
        fsm.createState('destroyed')
            .add(DeathThroes)
            .withInstance(new DeathThroes(3))
            .add(Display)
            .withInstance(new Display(node))
            .add(Animation)
            .withInstance(new Animation(deathView));

        asteroid
            .add(new Asteroid(fsm))
            .add(new Position(x, y, 0));
        //     .add(new Audio());

        fsm.changeState('alive');
        this._engine.addEntity(asteroid);
        return asteroid;
    }

    public createSpaceship(): ash.Entity {
        let spaceship: ash.Entity = this._spaceship;
        let fsm: ash.EntityStateMachine;
        if (!spaceship) {
            this._spaceship = spaceship = new ash.Entity();
            fsm = new ash.EntityStateMachine(spaceship);

            let node = new Node("spacesship");
            node.addComponent(SpaceshipView);
            fsm.createState('playing')
                .add(Motion)
                .withInstance(new Motion(0, 0, 0, 15))
                .add(MotionControls)
                .withInstance(new MotionControls(KeyCode.ARROW_LEFT, KeyCode.ARROW_RIGHT, KeyCode.ARROW_UP, 100, 3))
                .add(Gun)
                .withInstance(new Gun(8, 0, 0.3, 2))
                .add(GunControls)
                .withInstance(new GunControls(KeyCode.SPACE))
                .add(Collision)
                .withInstance(new Collision(9))
                .add(Display)
                .withInstance(new Display(node));

            node = new Node();
            const deathView = node.addComponent(SpaceshipDeathView);
            fsm.createState('destroyed')
                .add(DeathThroes)
                .withInstance(new DeathThroes(5))
                .add(Display)
                .withInstance(new Display(node))
                .add(Animation)
                .withInstance(new Animation(deathView));

            spaceship.add(new Spaceship(fsm));
        } else {
            fsm = spaceship.get(Spaceship)!.fsm;
            spaceship.get(DeathThroes).countdown = 5;
        }

        spaceship.add(new Position(0, 0, 0));
        // .add(new Audio());
        fsm.changeState('playing');
        this._engine.addEntity(this._spaceship);
        return this._spaceship;
    }

    public createUserBullet(gun: Gun, parentPosition: Position): ash.Entity {
        const cos: number = Math.cos(parentPosition.rotation);
        const sin: number = Math.sin(parentPosition.rotation);
        const node = new Node("bullet");
        node.addComponent(BulletView);
        const bullet: ash.Entity = new ash.Entity()
            .add(new Bullet(gun.bulletLifetime))
            .add(new Position(
                cos * gun.offsetFromParentX - sin * gun.offsetFromParentY + parentPosition.x,
                sin * gun.offsetFromParentX + cos * gun.offsetFromParentY + parentPosition.y,
                0,
            ))
            .add(new Collision(0))
            .add(new Motion(cos * 150, sin * 150, 0, 0))
            .add(new Display(node));
        this._engine.addEntity(bullet);
        return bullet;
    }
}